<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Linux/C 字符串操作函数速查手册</title>
<style>
    body { font-family: Arial, sans-serif; margin: 20px; background: #f9f9f9; }
    h1 { text-align: center; }
    h2 { margin-top: 40px; }
    table { border-collapse: collapse; width: 100%; margin-bottom: 30px; }
    th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
    th { background-color: #444; color: #fff; }
    .kernel { background-color: #d4f4dd; }    /* 内核专用浅绿 */
    .user { background-color: #dce9ff; }      /* 用户态浅蓝 */
    .shared { background-color: #fff8d6; }    /* 共用浅黄 */
    code { background: #eee; padding: 2px 4px; border-radius: 3px; }
    .desc { text-align:center; font-size: 12px; color: #555; margin-bottom: 10px; }
    .search-box { margin: 10px 0; padding: 5px; width: 300px; }
</style>
<script>
function tableSearch(inputId, tableId) {
    var input = document.getElementById(inputId);
    var filter = input.value.toLowerCase();
    var table = document.getElementById(tableId);
    var trs = table.getElementsByTagName("tr");

    for (var i = 1; i < trs.length; i++) { // 跳过表头
        var cells = trs[i].getElementsByTagName("td");
        var match = false;
        for (var j = 0; j < cells.length; j++) {
            if (cells[j].textContent.toLowerCase().indexOf(filter) > -1) {
                match = true;
                break;
            }
        }
        trs[i].style.display = match ? "" : "none";
    }
}
</script>
</head>
<body>

<h1>Linux/C 字符串操作函数速查手册</h1>
<div class="desc">SVG 时间轴展示了函数的出现年代和演进路径，颜色表示安全等级：红=高风险，橙=改进，绿=推荐安全API，蓝=辅助。</div>

<!-- SVG 时间轴 -->
<svg width="1000" height="200">
  <line x1="50" y1="100" x2="950" y2="100" stroke="black" stroke-width="2"/>
  <circle cx="50" cy="100" r="10" fill="red"/>
  <text x="50" y="60" text-anchor="middle" font-size="12" font-weight="bold">1970s strcpy</text>
  <text x="50" y="125" text-anchor="middle" font-size="10">ANSI C, 无长度检查</text>
  <circle cx="200" cy="100" r="10" fill="blue"/>
  <text x="200" y="60" text-anchor="middle" font-size="12" font-weight="bold">1980s strdup</text>
  <text x="200" y="125" text-anchor="middle" font-size="10">BSD/POSIX, 动态复制</text>
  <circle cx="350" cy="100" r="10" fill="red"/>
  <text x="350" y="60" text-anchor="middle" font-size="12" font-weight="bold">1989 strncpy</text>
  <text x="350" y="125" text-anchor="middle" font-size="10">ANSI C, 长度限制无\\0</text>
  <circle cx="500" cy="100" r="10" fill="orange"/>
  <text x="500" y="60" text-anchor="middle" font-size="12" font-weight="bold">1998 strlcpy</text>
  <text x="500" y="125" text-anchor="middle" font-size="10">OpenBSD, 保证\\0结尾</text>
  <circle cx="650" cy="100" r="10" fill="green"/>
  <text x="650" y="60" text-anchor="middle" font-size="12" font-weight="bold">1990s snprintf</text>
  <text x="650" y="125" text-anchor="middle" font-size="10">C99, 安全格式化</text>
  <circle cx="800" cy="100" r="10" fill="green"/>
  <text x="800" y="60" text-anchor="middle" font-size="12" font-weight="bold">~2000 scnprintf</text>
  <text x="800" y="125" text-anchor="middle" font-size="10">内核专用</text>
  <circle cx="950" cy="100" r="10" fill="green"/>
  <text x="950" y="60" text-anchor="middle" font-size="12" font-weight="bold">2016+ strscpy</text>
  <text x="950" y="125" text-anchor="middle" font-size="10">内核安全复制</text>
</svg>

<h2>按使用范围分类</h2>
<input type="text" id="searchClassify" class="search-box" placeholder="搜索分类表..." onkeyup="tableSearch('searchClassify','classifyTable')">
<table id="classifyTable">
<tr>
    <th>函数名</th>
    <th>主要特性</th>
    <th>类别</th>
</tr>
<tr class="shared"><td>strcpy</td><td>不检查长度，直接复制直到<code>\0</code>，高风险</td><td>共用</td></tr>
<tr class="shared"><td>strncpy</td><td>限制最大复制长度，但不保证终止符</td><td>共用</td></tr>
<tr class="shared"><td>strcat / strncat</td><td>拼接字符串，<code>strncat</code> 限制长度</td><td>共用</td></tr>
<tr class="user"><td>strdup</td><td>动态分配并复制字符串（malloc）</td><td>用户态专用</td></tr>
<tr class="shared"><td>strsep</td><td>解析分隔符，返回片段</td><td>共用</td></tr>
<tr class="kernel"><td>strlcpy</td><td>限长复制，保证<code>\0</code>结尾，返回源长度</td><td>内核专用（BSD用户态可用）</td></tr>
<tr class="kernel"><td>strscpy</td><td>安全复制，返回写入字节数或错误码</td><td>内核专用</td></tr>
<tr class="shared"><td>memcpy / memmove</td><td>内存块复制，支持重叠</td><td>共用</td></tr>
<tr class="shared"><td>memset</td><td>内存填充</td><td>共用</td></tr>
<tr class="shared"><td>strlen / strnlen</td><td>计算长度（可限制最大扫描）</td><td>共用</td></tr>
<tr class="shared"><td>strcmp / strncmp</td><td>比较字符串</td><td>共用</td></tr>
<tr class="shared"><td>snprintf</td><td>安全格式化输出，返回应写入长度</td><td>共用</td></tr>
<tr class="shared"><td>vsnprintf</td><td>带<code>va_list</code>的变种</td><td>共用</td></tr>
<tr class="kernel"><td>scnprintf</td><td>内核定制版<code>snprintf</code>，返回实际写入长度</td><td>内核专用</td></tr>
<tr class="kernel"><td>kstrdup</td><td>内核专用动态复制（kmalloc）</td><td>内核专用</td></tr>
<tr class="kernel"><td>kasprintf</td><td>内核专用分配+格式化输出</td><td>内核专用</td></tr>
<tr class="shared"><td>strchr / strrchr</td><td>查找字符首次/最后出现位置</td><td>共用</td></tr>
<tr class="shared"><td>strpbrk</td><td>查找目标串中的任意一个指定字符</td><td>共用</td></tr>
<tr class="shared"><td>strspn / strcspn</td><td>查找前缀匹配/不匹配集的长度</td><td>共用</td></tr>
</table>

<h2>API 原型与说明</h2>
<input type="text" id="searchAPI" class="search-box" placeholder="搜索API表..." onkeyup="tableSearch('searchAPI','apiTable')">
<table id="apiTable">
<tr>
    <th>函数名</th>
    <th>原型</th>
    <th>参数说明</th>
    <th>返回值说明</th>
    <th>适用范围</th>
</tr>
<tr class="shared"><td>strcpy</td><td><code>char *strcpy(char *dest, const char *src);</code></td>
<td>dest: 目标缓冲区<br>src: 源字符串</td>
<td>返回目标指针</td><td>共用</td></tr>
<tr class="user"><td>strdup</td><td><code>char *strdup(const char *s);</code></td>
<td>s: 源字符串</td><td>新分配字符串指针，失败返回NULL</td><td>用户态</td></tr>
<tr class="shared"><td>strncpy</td><td><code>char *strncpy(char *dest, const char *src, size_t n);</code></td>
<td>dest: 目标缓冲区<br>src: 源字符串<br>n: 最大复制长度</td>
<td>目标指针</td><td>共用</td></tr>
<tr class="kernel"><td>strscpy</td><td><code>ssize_t strscpy(char *dest, const char *src, size_t count);</code></td>
<td>dest: 目标缓冲区<br>src: 源字符串<br>count: 缓冲区总大小</td>
<td>写入字节数或-E2BIG表示截断</td><td>内核</td></tr>
<tr class="shared"><td>snprintf</td><td><code>int snprintf(char *str, size_t size, const char *fmt, ...);</code></td>
<td>str: 输出缓冲区<br>size: 缓冲区大小<br>fmt: 格式化字符串</td>
<td>欲写入总字节数（不含\0）</td><td>共用</td></tr>
<tr class="kernel"><td>scnprintf</td><td><code>int scnprintf(char *str, size_t size, const char *fmt, ...);</code></td>
<td>同snprintf</td><td>实际写入的字节数</td><td>内核</td></tr>
<tr class="kernel"><td>kstrdup</td><td><code>char *kstrdup(const char *s, gfp_t gfp);</code></td>
<td>s: 源字符串<br>gfp: 内核内存分配标志</td>
<td>新分配的字符串指针</td><td>内核</td></tr>
<tr class="kernel"><td>kasprintf</td><td><code>char *kasprintf(gfp_t gfp, const char *fmt, ...);</code></td>
<td>gfp: 分配标志<br>fmt: 格式化字符串</td>
<td>新分配的格式化输出字符串</td><td>内核</td></tr>
</table>

</body>
</html>